extends CharacterBody2D

############## 基本变量
# 超人模式
@export var is_super_man:bool = false

# 重力与常规速度
var gravity:float = 2000 # ProjectSettings.get_setting("physics/2d/default_gravity") * 2
var speed = 350

# 0.2秒加到最大速度
var accelerate = speed / 0.2 

# 精灵
@onready var sprite = $Sprite
@onready var animation_player = $AnimationPlayer

# 脸面朝向 1:右边 -1:左边
@export var face_direction:int = 1

############## 跳跃相关 
# 起跳速度
const jump_force:float = 900
# 按压时长
#var jump_pressed_time:float = 0 
# 从跳跃速度到为0所消耗的时间 v = gt  -900 = 2000*t 
var jump_max_time:float = jump_force/gravity
# 跳跃次数，最多2次。落在地板，天花板，墙才可以重置
var jump_num = 0
# 用于可提前发起起跳请求 - 例如在落地之前按下起跳键，在落地时即可起跳 - 手感丝滑
@onready var jump_request_timer:Timer = $Timers/JumpRequestTimer
@onready var jump_sound:AudioStreamPlayer2D = $Sound/JumpSound

#################### 坠落状态
# 郊狼时间: 用于在离开地板一段时间后仍然可跳
@onready var coyote_timer:Timer = $Timers/CoyoteTimer

#################### 冲锋
@onready var charge_timer:Timer = $Timers/ChargeTimer
# 冲刺次数; 最大1
var charge_num:int = 0
@export var charge_max_num:int = 1

#################### 死亡计时
var dead_time:float = 0

#################### 抓天花板
var prepare_catch_celling:bool = false

################### 墙
# 最后一次抓住墙的方向
var wall_direction = 0

#################### 状态机
enum Status{Idle=1,Walk,Jump,Charge,Death,Falling,OnWall,Celling,Unknown}
var play_status_before = Status.Idle
var play_status:Status = Status.Idle :
	set(next_status):
		if next_status != play_status:
			play_status_before = play_status
			play_status = next_status
			#状态变更时打印
			#print_play_status()

##################### 射线
@onready var ray_forward:RayCast2D = $Boxes/RayForward
var ray_direct_right = 50*Vector2.RIGHT
var ray_direct_left = 50*Vector2.LEFT
var ray_direct_up = 50*Vector2.UP
var ray_direct_down = 50*Vector2.DOWN

################## 伤害与受伤
@onready var hit_box = $Boxes/HitBox
@onready var hurt_box = $Boxes/HurtBox

######################################################### end 变量设置

func _ready():
	_force_face_direction(face_direction)

# 动画，使之更加生动有趣
func _tween_scale(target):
	var tween:Tween
	tween = get_tree().create_tween()
	tween.set_parallel(false)
	tween.set_ease (Tween.EASE_IN_OUT)
	#tween.set_trans(Tween.TRANS_BACK )
	tween.tween_property(sprite, "scale", target, 0.05)
	tween.tween_property(sprite, "scale", Vector2(1,1), 0.1)

# 物理桢，处理碰撞的逻辑
func _physics_process(_delta):
	var was_on_floor = is_on_floor()
	var was_on_wall = is_on_wall()
	var was_on_celling = is_on_ceiling_only()
	
	if was_on_floor || was_on_wall:
		_reset_jump_num()
		_reset_charge_num()
		
	if was_on_celling:
		_reset_jump_num()
	
	#var pre_velocity = velocity
	move_and_slide()
	
	if play_status == Status.Death:
		return
	
	var colider = get_last_slide_collision()

	# 地面上时刻
	if  is_on_floor():
		_reset_jump_num()
		_reset_charge_num()
		
		if velocity.x == 0:
			_to_idle_status()
		else:
			_to_walk_status()
		
		if not was_on_floor:
			# 落地时刻: 曾经不地上，但是现在在地上
			_tween_scale(Vector2(1.25,1/1.25))
		#else:
			## 一直在地上
			#pass
			
	elif was_on_floor:
		# 从地板离开: 曾经在地板上但是现在不在地上
		if play_status == Status.Jump:
			# 起跳时刻
			pass
		else:
			# 落崖时刻
			_to_falling_status()
	#else:
		# 原来不在地上，现在也不在地上
		#pass
	
	# 墙上时刻
	if is_on_wall_only():
		if play_status == Status.Jump || play_status == Status.Charge || play_status == Status.Falling:
			if colider!=null:
				wall_direction = colider.get_normal().x
				var ray_direction = ray_forward.target_position.x
				if ray_direction * wall_direction < 0:
					var is_find = ray_forward.is_colliding()
					if is_find:
						_to_onwall_status()
	else:
		# 落崖时刻
		if was_on_wall && play_status == Status.OnWall:
			_to_falling_status()
	
	# 头底时刻
	if is_on_ceiling_only():
		if prepare_catch_celling && play_status != Status.Celling:
			play_status = Status.Celling
	else:
		# 落崖时刻
		if was_on_celling && play_status == Status.Celling:
			_to_falling_status()
			
# 逻辑桢，处理画面的逻辑
func _process(delta):
	if play_status == Status.Charge:
		if charge_timer.time_left<=0:
			# 冲锋过后,做自由落体运动
			common_velocity(delta)
			
			# 二段跳
			if jump_request_timer.time_left>0:
				_to_jump_second_status()

	elif play_status == Status.Death:
		dead_time += delta
		velocity.x = 0
		if dead_time >= 0.1 and dead_time < 0.2:
			velocity.y = 0
		else:
			velocity.y += gravity * delta

	elif play_status == Status.Jump:
		common_velocity(delta)
		
		# 二段跳
		if jump_num < 2 && jump_request_timer.time_left>0:
			_to_jump_second_status()
			
	elif play_status == Status.OnWall:
		if is_on_wall() && jump_request_timer.time_left>0:
			# 从墙上发起跳跃
			velocity.x = wall_direction * speed * 1.2
			_to_jump_status()
		else:
			# 从墙上向下滑
			var direction = get_direction()
			if direction == 0:
				# 给予一个与墙相反的速度，以产生持续碰撞
				velocity.x = clamp(-wall_direction * speed / 10.0,-30,30)
			else:
				velocity.x = direction * speed
			
			# 以缓慢的速度向下坠落
			velocity.y = speed / 4.0
			
			# 如果挂不在墙上
			if ray_forward.target_position.x * wall_direction > 0:	
				_to_unknown_status()
				
	elif play_status == Status.Celling:
		if is_on_ceiling() && jump_request_timer.time_left>0:
			# 从天花板上
			_to_falling_from_celling()
		else:
			# 从天花板走动
			var direction = get_direction()
			velocity.x = direction * speed / 2.0
			velocity.y = -10
			
	elif  play_status == Status.Falling:
		#1. 速度通用处理
		common_velocity(delta)
		#2. 只支持二段跳
		if  coyote_timer.time_left>0 && jump_request_timer.time_left>0:
			_to_jump_second_status()
			
	elif play_status == Status.Walk or play_status == Status.Idle or Status.Unknown:
		#1. 速度通用处理
		common_velocity(delta)
			
		#2. 状态变更新
		var can_jump = is_on_floor() or coyote_timer.time_left>0
		if can_jump && jump_request_timer.time_left>0:
			_to_jump_status()
		elif jump_num < 2 && jump_request_timer.time_left>0:
			_to_jump_second_status()
	else:
		# 转成未知状态
		_to_unknown_status()

	# 播放动画
	play_animiation()

# 自由落地运动
func common_velocity(delta):
	var direction = get_direction()
	velocity.y += gravity * delta
	velocity.x = move_toward(velocity.x, direction * speed, accelerate * delta)

# 打印当前的状态
func print_play_status():
	if play_status_before == play_status:
		return
		
	if play_status == Status.Walk:
		print("Walk")
	elif play_status == Status.Jump:
		print("Jump")
	elif play_status == Status.Idle:
		print("Idle")
	elif play_status == Status.Charge:
		print("Charge")
	elif play_status == Status.OnWall:
		print("OnWall")
	elif play_status == Status.Falling:
		print("Falling")
	elif play_status == Status.Unknown:
		print("Unknown")
	elif play_status == Status.Death:
		print("Death")
	elif play_status == Status.Celling:
		print("Celling")
	else:
		print("invalid")

# 播放当前的角色的动画
func play_animiation():
	if play_status == Status.Walk:
		animation_player.play("walk")
	elif play_status == Status.Jump:
		animation_player.play("jump")
	elif play_status == Status.Idle:
		animation_player.play("idle")
	elif play_status == Status.Charge:
		animation_player.play("walk")
	elif play_status == Status.Falling:
		animation_player.play("walk")
	elif play_status == Status.OnWall:
		animation_player.play("jump")
	elif play_status == Status.Unknown:
		animation_player.play("walk")
	elif play_status == Status.Death:
		animation_player.play("death")
	elif play_status == Status.Celling:
		if velocity.x != 0:
			animation_player.play("celling_walk")
		else:
			animation_player.play("jump")
		

# 转为状态
#Status{Idle=1,Walk,Jump,Charge,Death,Falling,OnWall,Unknown}
func _to_walk_status():
	play_status = Status.Walk
	
func _to_idle_status():
	play_status = Status.Idle
	
func _to_unknown_status():
	play_status = Status.Unknown
	
func _to_onwall_status():
	play_status = Status.OnWall
	
func _to_falling_status():
	coyote_timer.start()
	play_status = Status.Falling

# 一段跳
func _to_jump_status():
	if jump_num >= 3:
		return
	_to_jump()

# 两段跳
func _to_jump_second_status():
	if jump_num >= 3:
		return
	jump_num = 2
	_to_jump()
	
func _to_jump():
	play_status = Status.Jump
	jump_request_timer.stop()
	coyote_timer.stop()
	
	jump_num += 1
	velocity.y = -jump_force
	_tween_scale(Vector2(1/1.25,1.25))

func _reset_jump_num():
	jump_num = 0

func _reset_charge_num():
	charge_num = 0

# 天花板上跳 - 跌落状态
func _to_falling_from_celling():
	play_status = Status.Falling
	jump_request_timer.stop()
	coyote_timer.stop()
	
	jump_num = 1
	velocity.y = jump_force
	_tween_scale(Vector2(1/1.25,1.25))

# 进入冲锋状态
func _to_charge_status_from_wall():
	_set_face_direction(wall_direction)
	_to_charge_status()
	
func _to_charge_status():
	charge_num +=1
	play_status = Status.Charge
	charge_timer.start()
	
	var direction = -1 if sprite.flip_h else 1	
	velocity = Vector2(speed * 5 * direction, 0)
	
	_tween_scale(Vector2(1.25,1/1.25))
	_super_time()

# 冲锋：时间结束
func _on_charge_timer_timeout():
	velocity.x = 0
	_normal_time()

# 冲锋：无敌时刻
func _super_time():
	hit_box.monitoring = false
	hurt_box.monitorable = false
	self.set_collision_layer_value(2,false)
	self.set_collision_mask_value(3,false)

# 冲锋：回归正常时刻
func _normal_time():
	hit_box.monitoring = true
	hurt_box.monitorable = true
	self.set_collision_layer_value(2,true)
	self.set_collision_mask_value(3,true)

# 死亡状态
func _to_death_status():
	velocity.y = -jump_force / 2.0
	play_status = Status.Death
	animation_player.play("death")

# 按键设置
# 玩家控制左右方向
func get_direction():
	var direction = Input.get_axis("left", "right")
	_set_face_direction(direction)
	return direction
	
# 脸面朝向
func _set_face_direction(direction):
	# 朝向
	if direction == 0:
		return
		
	if direction == face_direction:
		return
		
	#if direction != 1 && direction != -1:
		#direction = 1
		
	_force_face_direction(direction)
	
func _force_face_direction(direction):
	face_direction = direction
	
	# 精灵朝向
	sprite.flip_h = direction < 0
	
	#射线朝向
	if direction > 0:
		ray_forward.set_target_position(ray_direct_right)
	else:
		ray_forward.set_target_position(ray_direct_left)

func _unhandled_input(event):
	if play_status == Status.Death:
		return
	
	# 长跳
	if  event.is_action_pressed("jump"):
		#jump_pressed_time = 0
		jump_request_timer.start()
		jump_sound.play()

	# 短跳
	if jump_num < 3 && event.is_action_released("jump") and velocity.y < -jump_force / 2.0:
		velocity.y = -jump_force / 2.0
		#if jump_pressed_time < jump_max_time:
			# 百分比跳跃
			#velocity.y = -jump_force * jump_pressed_time / jump_max_time
	
	# 冲锋
	if play_status != Status.Charge &&  play_status != Status.Celling && charge_num < charge_max_num && event.is_action_pressed("mouse_right"):
		if play_status == Status.OnWall:
			_to_charge_status_from_wall()
		else:
			_to_charge_status()
	
	# 下墙或下天花板
	if  event.is_action_pressed("down"):
		if play_status==Status.Celling:
			velocity.y = 0
			_to_falling_status()
		if play_status==Status.OnWall:
			velocity.x = 0
			_to_falling_status()
			
	# 下墙或下天花板
	if  event.is_action_pressed("up"):
		if play_status!=Status.Celling:
			prepare_catch_celling = true
			
	if  event.is_action_released("up"):
		prepare_catch_celling = false
			

# 被别人击中
func _on_hurt_box_hurt(_hit_box):
	if is_super_man:
		print("oauch")
		return

	_to_death_status()
	#Engine.time_scale = 0.5
	await $AnimationPlayer.animation_finished
	Global.reload_world()

# 击中敌人
func _on_hit_box_hit():
	velocity.y = -jump_force / 2.0
	if not is_on_floor():
		_tween_scale(Vector2(1.25,1/1.25))


